<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 4.2.1">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">
  <meta name="google-site-verification" content="yF8HXsFuCTrePDFRnSkN2kkzV2ypBjtywzXsfVXOOV8">
  <meta name="baidu-site-verification" content="VxrVAiQAZ9">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"yang0033.gitee.io","root":"/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":true,"preload":true},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="iOS性能优化 — 界面卡顿产生的原因和解决方案卡顿产生的原因和解决方案在 VSync 信号到来后，系统图形服务会通过 CADisplayLink 等机制通知 App，App 主线程开始在 CPU">
<meta property="og:type" content="article">
<meta property="og:title" content="iOS性能优化 --- 界面卡顿产生的原因和解决方案">
<meta property="og:url" content="http://yang0033.gitee.io/2015/11/29/ios%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96_---_%E7%95%8C%E9%9D%A2%E5%8D%A1%E9%A1%BF%E4%BA%A7%E7%94%9F%E7%9A%84%E5%8E%9F%E5%9B%A0%E5%92%8C%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/index.html">
<meta property="og:site_name" content="SuperYang&#96;s Blog">
<meta property="og:description" content="iOS性能优化 — 界面卡顿产生的原因和解决方案卡顿产生的原因和解决方案在 VSync 信号到来后，系统图形服务会通过 CADisplayLink 等机制通知 App，App 主线程开始在 CPU">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://blog.ibireme.com/wp-content/uploads/2015/11/ios_frame_drop.png">
<meta property="article:published_time" content="2015-11-28T16:16:12.000Z">
<meta property="article:modified_time" content="2020-07-09T07:54:18.921Z">
<meta property="article:author" content="SuperYang">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://blog.ibireme.com/wp-content/uploads/2015/11/ios_frame_drop.png">

<link rel="canonical" href="http://yang0033.gitee.io/2015/11/29/ios%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96_---_%E7%95%8C%E9%9D%A2%E5%8D%A1%E9%A1%BF%E4%BA%A7%E7%94%9F%E7%9A%84%E5%8E%9F%E5%9B%A0%E5%92%8C%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>iOS性能优化 --- 界面卡顿产生的原因和解决方案 | SuperYang`s Blog</title>
  
    <script async src="https://www.googletagmanager.com/gtag/js?id=UA-71812998-3"></script>
    <script>
      if (CONFIG.hostname === location.hostname) {
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());
        gtag('config', 'UA-71812998-3');
      }
    </script>


  <script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?8ff3295288514de6f89f314c23ee40ab";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>




  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">SuperYang`s Blog</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">落魄程序员在线炒饭</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
        <li class="menu-item menu-item-message">

    <a href="/message/" rel="section"><i class="fa fa-music fa-fw"></i>留言板 | Music</a>

  </li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result">
  <div id="no-result">
    <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
  </div>
</div>

    </div>
  </div>

</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://yang0033.gitee.io/2015/11/29/ios%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96_---_%E7%95%8C%E9%9D%A2%E5%8D%A1%E9%A1%BF%E4%BA%A7%E7%94%9F%E7%9A%84%E5%8E%9F%E5%9B%A0%E5%92%8C%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="https://tva2.sinaimg.cn/crop.259.17.290.290.180/c2abdfa9jw8ez7appr3p2j20g40a2t91.jpg?KID=imgbed,tva&Expires=1594186820&ssig=BdxhDdi1Ti">
      <meta itemprop="name" content="SuperYang">
      <meta itemprop="description" content="牛肉炒饭不要香菜，老板收钱">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="SuperYang`s Blog">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          iOS性能优化 --- 界面卡顿产生的原因和解决方案
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2015-11-29 00:16:12" itemprop="dateCreated datePublished" datetime="2015-11-29T00:16:12+08:00">2015-11-29</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2020-07-09 15:54:18" itemprop="dateModified" datetime="2020-07-09T15:54:18+08:00">2020-07-09</time>
              </span>

          
            <span id="/2015/11/29/ios%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96_---_%E7%95%8C%E9%9D%A2%E5%8D%A1%E9%A1%BF%E4%BA%A7%E7%94%9F%E7%9A%84%E5%8E%9F%E5%9B%A0%E5%92%8C%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/" class="post-meta-item leancloud_visitors" data-flag-title="iOS性能优化 --- 界面卡顿产生的原因和解决方案" title="阅读次数">
              <span class="post-meta-item-icon">
                <i class="fa fa-eye"></i>
              </span>
              <span class="post-meta-item-text">阅读次数：</span>
              <span class="leancloud-visitors-count"></span>
            </span>
  
  <span class="post-meta-item">
    
      <span class="post-meta-item-icon">
        <i class="far fa-comment"></i>
      </span>
      <span class="post-meta-item-text">Valine：</span>
    
    <a title="valine" href="/2015/11/29/ios%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96_---_%E7%95%8C%E9%9D%A2%E5%8D%A1%E9%A1%BF%E4%BA%A7%E7%94%9F%E7%9A%84%E5%8E%9F%E5%9B%A0%E5%92%8C%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/#valine-comments" itemprop="discussionUrl">
      <span class="post-comments-count valine-comment-count" data-xid="/2015/11/29/ios%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96_---_%E7%95%8C%E9%9D%A2%E5%8D%A1%E9%A1%BF%E4%BA%A7%E7%94%9F%E7%9A%84%E5%8E%9F%E5%9B%A0%E5%92%8C%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/" itemprop="commentCount"></span>
    </a>
  </span>
  
  <br>
            <span class="post-meta-item" title="本文字数">
              <span class="post-meta-item-icon">
                <i class="far fa-file-word"></i>
              </span>
                <span class="post-meta-item-text">本文字数：</span>
              <span>3.6k</span>
            </span>
            <span class="post-meta-item" title="阅读时长">
              <span class="post-meta-item-icon">
                <i class="far fa-clock"></i>
              </span>
                <span class="post-meta-item-text">阅读时长 &asymp;</span>
              <span>3 分钟</span>
            </span>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <h1 id="iOS性能优化-—-界面卡顿产生的原因和解决方案"><a href="#iOS性能优化-—-界面卡顿产生的原因和解决方案" class="headerlink" title="iOS性能优化 — 界面卡顿产生的原因和解决方案"></a>iOS性能优化 — 界面卡顿产生的原因和解决方案</h1><h2 id="卡顿产生的原因和解决方案"><a href="#卡顿产生的原因和解决方案" class="headerlink" title="卡顿产生的原因和解决方案"></a>卡顿产生的原因和解决方案</h2><p><img src="http://blog.ibireme.com/wp-content/uploads/2015/11/ios_frame_drop.png" alt="image"><br>在 VSync 信号到来后，系统图形服务会通过 CADisplayLink 等机制通知 App，App 主线程开始在 CPU <a id="more"></a> 中计算显示内容，比如视图的创建、布局计算、图片解码、文本绘制等。随后 CPU 会将计算好的内容提交到 GPU 去，由 GPU 进行变换、合成、渲染。随后 GPU 会把渲染结果提交到帧缓冲区去，等待下一次 VSync 信号到来时显示到屏幕上。由于垂直同步的机制，如果在一个 VSync 时间内，CPU 或者 GPU 没有完成内容提交，则那一帧就会被丢弃，等待下一次机会再显示，而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。<br>CPU 和 GPU 不论哪个阻碍了显示流程，都会造成掉帧现象。所以开发时，也需要分别对 CPU 和 GPU 压力进行评估和优化。</p>
<h3 id="CPU-资源消耗原因和解决方案"><a href="#CPU-资源消耗原因和解决方案" class="headerlink" title="CPU 资源消耗原因和解决方案"></a>CPU 资源消耗原因和解决方案</h3><h4 id="对象创建"><a href="#对象创建" class="headerlink" title="对象创建"></a>对象创建</h4><p>对象创建时，尽量使用轻量级对象，比如CALayer代替UIView，当你的视图并不需要处理触摸事件时。还有对象创建尽量放在后台线程进行，所有Storyboard创建对象时，其资源消耗要大得多。性能敏感的界面不提倡。<br>尽量推迟对象的创建时间，并将创建动作分散到多个任务中。如果对象可以复用，并且复用的代价小于创建和销毁的对象时，这类对象尽量放在一个缓存池中进行复用。比如官方的tableViewCell缓存。</p>
<h4 id="对象调整"><a href="#对象调整" class="headerlink" title="对象调整"></a>对象调整</h4><p>尤其是CALayer，他的内部没有属性，当调用他的属性时，内部会使用runtime的resolveInstanceMethod方法为对象临时添加一个方法，并把对象属性值保存在一个内部Dictionary里，同时还会通知代理创建动画等，消耗大量资源。并且UIView的一些和显示相关的属性（frame/bounds/transform）等属性实际上都是CALayer属性上映射来的，消耗的资源远大于其他属性，所以在应用尽量不要过多的修改这些属性。</p>
<p>当视图层次调整，UIView和CALayer之间会出现很多方法与通知，所以在性能优化时，应该尽量避免视图层次调整，添加和移除视图。（考虑使用hide代替）</p>
<h4 id="对象销毁"><a href="#对象销毁" class="headerlink" title="对象销毁"></a>对象销毁</h4><p>通常当容器类持有大量对象时，其销毁对象的资源消耗非常明显，同样，如果对象可以放到后台线程中释放，会减少对主线程的资源消耗小tips：将对象用block捕获，然后扔到后台队列中去随便发送个消息以避免编译器警告，就可以保证对象在后台销毁了</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">NSArray</span> *tmp = <span class="keyword">self</span>.array;</span><br><span class="line"><span class="keyword">self</span>.array = <span class="literal">nil</span>;</span><br><span class="line"><span class="built_in">dispatch_async</span>(queue, ^&#123;</span><br><span class="line">    [tmp <span class="keyword">class</span>];</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h4 id="布局计算"><a href="#布局计算" class="headerlink" title="布局计算"></a>布局计算</h4><p>App中最为常见的消耗CPU资源的位置，如果能在后台线程提现计算好试图布局，并对试图布局进行缓存，那么这个地方就基本不会产生太多性能问题。<br>不论采用什么技术惊醒视图布局，最终都会落在frame/bounds/center等属性的调整上，上面也说过，对这些属性的调整消耗资源，所以需要在后台提前计算好布局，在需要时一次性调整好这些属性，而不是多次频繁地计算和调整这些属性。</p>
<h4 id="AutoLayout"><a href="#AutoLayout" class="headerlink" title="AutoLayout"></a>AutoLayout</h4><p>这项技术本身受到苹果官方的提倡技术，在大部分情况下有着不俗的表现，但是在复杂的界面中会产生严重的性能问题，并且随着视图的增长，对CPU的消耗呈指数型增长,具体可见<a href="http://pilky.me/36/" target="_blank" rel="noopener">http://pilky.me/36/</a>.如果对于frame等属性的调整可以使用一些工具方法代替，或者使用ComponentKit或AsyncDisplayKit等框架</p>
<h4 id="文本计算"><a href="#文本计算" class="headerlink" title="文本计算"></a>文本计算</h4><p>当界面中包含大量文本计算时（微博盆友圈），文本的宽高计算会消耗大量的资源，当只有一些简单的文本展示使用UILable的内部实现方式用 <code>[NSAttributedString boundingRectWithSize:options:context:]</code>来计算文本宽高, 用<code>-[NSAttributedString drawWithRect:options:context:]</code> 来绘制文本.尽管性能表现不错，但仍需放到后台避免阻塞主线程。</p>
<h4 id="文本渲染"><a href="#文本渲染" class="headerlink" title="文本渲染"></a>文本渲染</h4><p>屏幕上能看到的所有文本控件，包括UIWebView都是底层通过CoreText排版，绘制为Bitmap显示的，常见的控件中UILabel、UITextView文本控件，都是在主线程排版的，当显示大量文本时，CUP压力会很大，对此的解决方案一般都是自定义文本控件，用TextKit或底层的CoreText对文本异步绘制，实现非常麻烦，但优势也是显而易见的，CoreText创建好后，能获取文本的宽高信息，避免了多次计算（UILabel调整尺寸时计算一遍，UILabel绘制时内存再算一遍），CoreText对象内存占用较少，可以缓存下来多次渲染。</p>
<h4 id="图片的解码"><a href="#图片的解码" class="headerlink" title="图片的解码"></a>图片的解码</h4><p>用 UIImage 或CGImageSource创建图片时，图片不会立即解码。图片设置到UIImageView或者CALayer.contents中去，并且CALayer被提交到GPU之前CGImage中的数据才会得到解码。这一步是发生在主线程的，并且不可避免。如果要想绕开这个机制，常见的做法是在后台线程先把图片绘制到CGBitMapContext中，然后从BitMap中直接创建图片，目前网络图片库都自带这个功能。</p>
<h4 id="图像的绘制"><a href="#图像的绘制" class="headerlink" title="图像的绘制"></a>图像的绘制</h4><p>图像的绘制通常都是以CG开头的方法吧图像绘制到画布中，然后从画布创建图片并显示的过程。常见为<code>[UIView drawRect:]</code>里面。由于CoreGraphic方法通常都是Thread Safe，所以图像绘制很容易放置到后台执行。一个简单的异步绘制过程大致如下（实际情况更加复杂，但是原理基本一致）</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="keyword">void</span>)display &#123;</span><br><span class="line">    <span class="built_in">dispatch_async</span>(backgroundQueue, ^&#123;</span><br><span class="line">        <span class="built_in">CGContextRef</span> ctx = <span class="built_in">CGBitmapContextCreate</span>(...);</span><br><span class="line">        <span class="comment">// draw in context...</span></span><br><span class="line">        <span class="built_in">CGImageRef</span> img = <span class="built_in">CGBitmapContextCreateImage</span>(ctx);</span><br><span class="line">        <span class="built_in">CFRelease</span>(ctx);</span><br><span class="line">        <span class="built_in">dispatch_async</span>(mainQueue, ^&#123;</span><br><span class="line">            layer.contents = img;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="GPU-资源消耗原因和解决"><a href="#GPU-资源消耗原因和解决" class="headerlink" title="GPU 资源消耗原因和解决"></a>GPU 资源消耗原因和解决</h3><p>方案<br>相对于 CPU 来说，GPU 能干的事情比较单一：接收提交的纹理（Texture）和顶点描述（三角形），应用变换（transform）、混合并渲染，然后输出到屏幕上。通常你所能看到的内容，主要也就是纹理（图片）和形状（三角模拟的矢量图形）两类。</p>
<h4 id="纹理的渲染"><a href="#纹理的渲染" class="headerlink" title="纹理的渲染"></a>纹理的渲染</h4><p>所有的 Bitmap，包括图片、文本、栅格化的内容，最终都要由内存提交到显存，绑定为 GPU Texture。不论是提交到显存的过程，还是 GPU 调整和渲染 Texture 的过程，都要消耗不少 GPU 资源。当在较短时间显示大量图片时（比如 TableView 存在非常多的图片并且快速滑动时），CPU 占用率很低，GPU 占用非常高，界面仍然会掉帧。避免这种情况的方法只能是尽量减少在短时间内大量图片的显示，<em>尽可能将多张图片合成为一张进行显示</em>。</p>
<p>当图片过大，超过 GPU 的最大纹理尺寸时，图片需要先由 CPU 进行预处理，这对 CPU 和 GPU 都会带来额外的资源消耗。目前来说，iPhone 4S 以上机型，纹理尺寸上限都是 4096x4096，更详细的资料可以看这里：<a href="http://iosres.com/" target="_blank" rel="noopener">iosres.com</a>。所以，尽量不要让图片和视图的大小超过这个值。</p>
<h4 id="视图的混合"><a href="#视图的混合" class="headerlink" title="视图的混合"></a>视图的混合</h4><p>当多个视图（CALayer）重叠在一起时，GPU会首先将她们混合起来，这样会消耗很多GPU资源，为了减轻这种情况的消耗，应该尽量减少视图数量和层次，并在不透明的视图中标明opaque属性为YES以避免Alpha通道的合成（告诉系统这个视图后面没有其他需要渲染的视图，避免多余的性能消耗），也可以用之前的方法将多个视图预先合成为一张图。</p>
<h4 id="图形的生成"><a href="#图形的生成" class="headerlink" title="图形的生成"></a>图形的生成</h4><p>CALayer 的 border、圆角、阴影、遮罩（mask），CASharpLayer 的矢量图形显示，通常会触发离屏渲染（offscreen rendering），而离屏渲染通常发生在 GPU 中。当一个列表视图中出现大量圆角的 CALayer，并且快速滑动时，可以观察到 GPU 资源已经占满，而 CPU 资源消耗很少。这时界面仍然能正常滑动，但平均帧数会降到很低。为了避免这种情况，可以尝试开启 <strong><code>CALayer.shouldRasterize</code></strong> 属性，但这会把原本离屏渲染的操作转嫁到 CPU 上去。对于只需要圆角的某些场合，也可以用<strong><code>一张已经绘制好的圆角图</code></strong>片覆盖到原本视图上面来模拟相同的视觉效果。最彻底的解决办法，就是把需要显示的图形在后台线程绘制为图片，避免使用圆角、阴影、遮罩等属性。</p>

    </div>

    
    
    
        

  <div class="followme">
    <p>欢迎关注我的其它发布渠道</p>

    <div class="social-list">

        <div class="social-item">
          <a target="_blank" class="social-link" href="https://www.jianshu.com/u/f37a8f0ba6f8">
            <span class="icon">
              <i class="fas fa-book"></i>
            </span>

            <span class="label">简书</span>
          </a>
        </div>
    </div>
  </div>


      <footer class="post-footer">

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2015/11/24/%E5%85%B3%E4%BA%8Empmovieplayerviewcontroller%E6%92%AD%E6%94%BE%E7%BB%93%E6%9D%9F%E5%90%8E%E8%87%AA%E5%8A%A8%E6%B6%88%E5%A4%B1%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/" rel="prev" title="关于MPMoviePlayerViewController播放结束后自动消失的解决方案">
      <i class="fa fa-chevron-left"></i> 关于MPMoviePlayerViewController播放结束后自动消失的解决方案
    </a></div>
      <div class="post-nav-item">
    <a href="/2015/12/04/%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE%E9%9B%86%E6%88%90/" rel="next" title="百度地图集成">
      百度地图集成 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          
    <div class="comments" id="valine-comments"></div>

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#iOS性能优化-—-界面卡顿产生的原因和解决方案"><span class="nav-number">1.</span> <span class="nav-text">iOS性能优化 — 界面卡顿产生的原因和解决方案</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#卡顿产生的原因和解决方案"><span class="nav-number">1.1.</span> <span class="nav-text">卡顿产生的原因和解决方案</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#CPU-资源消耗原因和解决方案"><span class="nav-number">1.1.1.</span> <span class="nav-text">CPU 资源消耗原因和解决方案</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#对象创建"><span class="nav-number">1.1.1.1.</span> <span class="nav-text">对象创建</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#对象调整"><span class="nav-number">1.1.1.2.</span> <span class="nav-text">对象调整</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#对象销毁"><span class="nav-number">1.1.1.3.</span> <span class="nav-text">对象销毁</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#布局计算"><span class="nav-number">1.1.1.4.</span> <span class="nav-text">布局计算</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#AutoLayout"><span class="nav-number">1.1.1.5.</span> <span class="nav-text">AutoLayout</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#文本计算"><span class="nav-number">1.1.1.6.</span> <span class="nav-text">文本计算</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#文本渲染"><span class="nav-number">1.1.1.7.</span> <span class="nav-text">文本渲染</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#图片的解码"><span class="nav-number">1.1.1.8.</span> <span class="nav-text">图片的解码</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#图像的绘制"><span class="nav-number">1.1.1.9.</span> <span class="nav-text">图像的绘制</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#GPU-资源消耗原因和解决"><span class="nav-number">1.1.2.</span> <span class="nav-text">GPU 资源消耗原因和解决</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#纹理的渲染"><span class="nav-number">1.1.2.1.</span> <span class="nav-text">纹理的渲染</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#视图的混合"><span class="nav-number">1.1.2.2.</span> <span class="nav-text">视图的混合</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#图形的生成"><span class="nav-number">1.1.2.3.</span> <span class="nav-text">图形的生成</span></a></li></ol></li></ol></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="SuperYang"
      src="https://tva2.sinaimg.cn/crop.259.17.290.290.180/c2abdfa9jw8ez7appr3p2j20g40a2t91.jpg?KID=imgbed,tva&Expires=1594186820&ssig=BdxhDdi1Ti">
  <p class="site-author-name" itemprop="name">SuperYang</p>
  <div class="site-description" itemprop="description">牛肉炒饭不要香菜，老板收钱</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">39</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">5</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">31</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/yangchao0033" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;yangchao0033" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="mailto:ygrfwyc@gmail.com" title="E-Mail → mailto:ygrfwyc@gmail.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>E-Mail</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://weibo.com/yangchao0033" title="Weibo → https:&#x2F;&#x2F;weibo.com&#x2F;yangchao0033" rel="noopener" target="_blank"><i class="fab fa-weibo fa-fw"></i>Weibo</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://www.jianshu.com/u/f37a8f0ba6f8" title="简书 → https:&#x2F;&#x2F;www.jianshu.com&#x2F;u&#x2F;f37a8f0ba6f8" rel="noopener" target="_blank"><i class="fas fa-book fa-fw"></i>简书</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://t.me/superYang0033" title="Telegram → https:&#x2F;&#x2F;t.me&#x2F;superYang0033" rel="noopener" target="_blank"><i class="fab fa-telegram fa-fw"></i>Telegram</a>
      </span>
  </div>



      </div>

      
        <div id="music163player">
        <iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=350 height=86 src="//music.163.com/outchain/player?type=2&id=31473269&auto=0&height=66">
        </iframe>
        </div>
    </div>

    

  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">SuperYang</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-area"></i>
    </span>
    <span title="站点总字数">157k</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
    <span title="站点阅读时长">2:23</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Gemini</a> 强力驱动
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  
  <script>
    (function(){
      var canonicalURL, curProtocol;
      //Get the <link> tag
      var x=document.getElementsByTagName("link");
		//Find the last canonical URL
		if(x.length > 0){
			for (i=0;i<x.length;i++){
				if(x[i].rel.toLowerCase() == 'canonical' && x[i].href){
					canonicalURL=x[i].href;
				}
			}
		}
    //Get protocol
	    if (!canonicalURL){
	    	curProtocol = window.location.protocol.split(':')[0];
	    }
	    else{
	    	curProtocol = canonicalURL.split(':')[0];
	    }
      //Get current URL if the canonical URL does not exist
	    if (!canonicalURL) canonicalURL = window.location.href;
	    //Assign script content. Replace current URL with the canonical URL
      !function(){var e=/([http|https]:\/\/[a-zA-Z0-9\_\.]+\.baidu\.com)/gi,r=canonicalURL,t=document.referrer;if(!e.test(r)){var n=(String(curProtocol).toLowerCase() === 'https')?"https://sp0.baidu.com/9_Q4simg2RQJ8t7jm9iCKT-xh_/s.gif":"//api.share.baidu.com/s.gif";t?(n+="?r="+encodeURIComponent(document.referrer),r&&(n+="&l="+r)):r&&(n+="?l="+r);var i=new Image;i.src=n}}(window);})();
  </script>




  
<script src="/js/local-search.js"></script>













  

  

  

  <script async src="/js/cursor/fireworks.js"></script>



<script>
NexT.utils.loadComments(document.querySelector('#valine-comments'), () => {
  NexT.utils.getScript('//unpkg.com/valine/dist/Valine.min.js', () => {
    var GUEST = ['nick', 'mail', 'link'];
    var guest = 'nick,mail,link';
    guest = guest.split(',').filter(item => {
      return GUEST.includes(item);
    });
    new Valine({
      el         : '#valine-comments',
      verify     : true,
      notify     : true,
      appId      : 'zizRqalUJY55Xc5oBJKbhxpV-gzGzoHsz',
      appKey     : '64gPcVi8lIA8zVGpBSEk7uKu',
      placeholder: "留言板无需注册登录，快来评论吧。。",
      avatar     : 'mm',
      meta       : guest,
      pageSize   : '10' || 10,
      visitor    : true,
      lang       : '' || 'zh-cn',
      path       : location.pathname,
      recordIP   : false,
      serverURLs : ''
    });
  }, window.Valine);
});
</script>

</body>
</html>
